package utils

import (
	"errors"
	"reflect"
	"strconv"
	"strings"
)

type Rules map[string][]string

func NotEmpty() string {
	return "notEmpty"
}

// 校验方法
//
// @params {interface{}} st 入参实例
// @params {map[string][]string} 规则 map
func Verify(st interface{}, roleMap Rules) (err error) {
	/*
		1. 创建一个 map 其中包含了一些比较操作符，用于后续的字段值比较验证。
		2. 通过反射获取类型和值，并获取 struct
			a. 若非结构体类型，则返回错误
			b. 若是结构体类型，继续后续逻辑
		3. 遍历结构体的所有字段，对每个字段进行验证
		4. 判断字段是否存在验证规则
			a. 若存在，则依次检查每个验证规则
				i. 若存在「notEmpty」规则，并且字段为空，则返回相应信息，表示该字段不能为空。
				ii. 如果存在其它比较验证规则（如长度货值范围的验证），则进行相应的比较验证，如果验证不通过，则返回相应的错误信息，指示验证未通过。
		5. 所有字段通校验，函数返回 nil，表示验证通过；否则返回相应信息，表示验证未通过。
	*/
	compareMap := map[string]bool{
		"lt": true,
		"le": true,
		"eq": true,
		"ne": true,
		"ge": true,
		"gt": true,
	}

	typ := reflect.TypeOf(st)
	val := reflect.ValueOf(st)

	kd := val.Kind()
	if kd != reflect.Struct {
		return errors.New("expect struct")
	}

	// 遍历结构体内所有的字段
	numField := typ.NumField()
	for i := 0; i < numField; i++ {
		targetVal := typ.Field(i)
		fieldVal := val.Field(i)

		if len(roleMap[targetVal.Name]) > 0 {
			for _, rule := range roleMap[targetVal.Name] {
				switch {
				case rule == "notEmpty":
					if isFieldEmpty(fieldVal) {
						return errors.New(targetVal.Name + " can not be empty")
					}
				// 根据类型自动校验长度和数字
				case compareMap[strings.Split(rule, "=")[0]]:
					if !isFieldCompare(fieldVal, rule) {
						return errors.New(targetVal.Name + " " + rule + " failed")
					}
				}
			}
		}
	}

	return nil
}

// 字段是否为空
func isFieldEmpty(fv reflect.Value) bool {
	switch fv.Kind() {
	case reflect.String:
		return fv.Len() == 0
	case reflect.Bool:
		return !fv.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return fv.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return fv.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return fv.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return fv.IsNil()
	}

	return reflect.DeepEqual(fv.Interface(), reflect.Zero(fv.Type()).Interface())
}

// 根据类型自动校验长度和数字
func isFieldCompare(fv reflect.Value, rule string) bool {
	switch fv.Kind() {
	case reflect.String, reflect.Slice, reflect.Array:
		return compare(fv.Len(), rule)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return compare(fv.Int(), rule)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return compare(fv.Uint(), rule)
	case reflect.Float32, reflect.Float64:
		return compare(fv.Float(), rule)
	}

	return false
}

// 用于比较变量值的函数，使用了反射来处理各种类型的变量，并根据输入的字符串条件进行相应的比较操作。这样设计可以让函数灵活地处理不同类型的数据，并进行相应的比较。
//
// @params {interface{}} val
// @params {string} verifyStr
func compare(val interface{}, verifyStr string) bool {
	// 1. 首先，通过 strings.Split 函数将 VerifyStr 按照 "=" 分割成数组 VerifyStrArr，以便获取比较条件和数值。
	// 2. 使用 reflect.ValueOf 来获取 value 的反射值 val。
	// 3. 通过 switch val.Kind() 来判断 value 的类型，根据不同类型的值执行相应的比较逻辑。
	// 4. 如果 value 是整数类型（包括有符号和无符号整数），则将 VerifyStrArr[1] 解析为整数，并根据 VerifyStrArr[0] 来判断进行 <, <=, ==, !=, >=, > 六种比较操作。
	// 5. 如果 value 是浮点数类型，则将 VerifyStrArr[1] 解析为浮点数，并进行类似的比较操作。
	// 6. 如果 value 的类型不是整数或浮点数，那么就返回 false，表示无法比较。
	verifyStrArr := strings.Split(verifyStr, "=")
	v := reflect.ValueOf(val)

	switch v.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		vint, verr := strconv.ParseInt(verifyStrArr[1], 10, 64)
		if verr != nil {
			return false
		}

		switch {
		case verifyStrArr[0] == "lt":
			return v.Int() < vint
		case verifyStrArr[0] == "le":
			return v.Int() <= vint
		case verifyStrArr[0] == "eq":
			return v.Int() == vint
		case verifyStrArr[0] == "ne":
			return v.Int() != vint
		case verifyStrArr[0] == "ge":
			return v.Int() >= vint
		case verifyStrArr[0] == "gt":
			return v.Int() > vint
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		vint, verr := strconv.Atoi(verifyStrArr[1])
		if verr != nil {
			return false
		}

		switch {
		case verifyStrArr[0] == "lt":
			return v.Uint() < uint64(vint)
		case verifyStrArr[0] == "le":
			return v.Uint() <= uint64(vint)
		case verifyStrArr[0] == "eq":
			return v.Uint() == uint64(vint)
		case verifyStrArr[0] == "ne":
			return v.Uint() != uint64(vint)
		case verifyStrArr[0] == "ge":
			return v.Uint() >= uint64(vint)
		case verifyStrArr[0] == "gt":
			return v.Uint() > uint64(vint)
		}
	case reflect.Float32, reflect.Float64:
		vfloat, verr := strconv.ParseFloat(verifyStrArr[1], 64)
		if verr != nil {
			return false
		}

		switch {
		case verifyStrArr[0] == "lt":
			return v.Float() < vfloat
		case verifyStrArr[0] == "le":
			return v.Float() <= vfloat
		case verifyStrArr[0] == "eq":
			return v.Float() == vfloat
		case verifyStrArr[0] == "ne":
			return v.Float() != vfloat
		case verifyStrArr[0] == "ge":
			return v.Float() >= vfloat
		case verifyStrArr[0] == "gt":
			return v.Float() > vfloat
		}
	}

	return false
}
